package com.thaiopensource.validate.picl;

abstract class PathPattern extends Pattern
{
  private final String [] names;
  private final boolean [] descendantsOrSelf;
  static final String ANY = "#any";

  PathPattern (final String [] names, final boolean [] descendantsOrSelf)
  {
    this.names = names;
    this.descendantsOrSelf = descendantsOrSelf;
  }

  abstract boolean isAttribute ();

  @Override
  boolean matches (final Path path, final int rootDepth)
  {
    return (isAttribute () == path.isAttribute () && matchSegment (path,
                                                                   rootDepth,
                                                                   path.length () - rootDepth,
                                                                   0,
                                                                   names.length >> 1,
                                                                   false));
  }

  private boolean matchSegment (final Path path,
                                int pathStartIndex,
                                int pathLength,
                                int patternStartIndex,
                                int patternLength,
                                boolean ignoreRightmostDescendantsOrSelf)
  {
    if (patternLength > pathLength)
      return false;
    while (patternLength > 0 &&
           (ignoreRightmostDescendantsOrSelf || !descendantsOrSelf[patternStartIndex + patternLength]))
    {
      if (!matchStep (path, pathStartIndex + pathLength - 1, patternStartIndex + patternLength - 1))
        return false;
      pathLength--;
      patternLength--;
      ignoreRightmostDescendantsOrSelf = false;
    }
    while (patternLength > 0 && !descendantsOrSelf[patternStartIndex])
    {
      if (!matchStep (path, pathStartIndex, patternStartIndex))
        return false;
      pathStartIndex++;
      patternStartIndex++;
      pathLength--;
      patternLength--;
    }
    if (patternLength == 0)
      return descendantsOrSelf[patternStartIndex] || pathLength == 0;
    for (pathLength--; pathLength >= patternLength; pathLength--)
      if (matchSegment (path, pathStartIndex, pathLength, patternStartIndex, patternLength, true))
        return true;
    return false;
  }

  private boolean matchStep (final Path path, final int pathIndex, int patternIndex)
  {
    patternIndex *= 2;
    return (matchName (path.getNamespaceUri (pathIndex), names[patternIndex]) && matchName (path.getLocalName (pathIndex),
                                                                                            names[patternIndex + 1]));
  }

  private static boolean matchName (final String str, final String pattern)
  {
    if (pattern == PathPattern.ANY)
      return true;
    return str.equals (pattern);
  }

  @Override
  public String toString ()
  {
    final StringBuffer buf = new StringBuffer ();
    for (int i = 0, j = 0; i < names.length; i += 2, j++)
    {
      if (j != 0)
        buf.append (descendantsOrSelf[j] ? "//" : "/");
      else
        if (descendantsOrSelf[0])
          buf.append (".//");
      if (isAttribute () && i + 2 == names.length)
        buf.append ('@');
      if (names[i] == ANY)
        buf.append ('*');
      else
      {
        if (names[i].length () != 0)
        {
          buf.append ('{');
          buf.append (names[i]);
          buf.append ('}');
        }
        buf.append (names[i + 1] == ANY ? "*" : names[i + 1]);
      }
    }
    if (names.length == 0)
      buf.append (descendantsOrSelf[0] ? ".//." : ".");
    else
      if (descendantsOrSelf[descendantsOrSelf.length - 1])
        buf.append ("//.");
    return buf.toString ();
  }
}
